import json
import pandas as pd
import numpy as np

from sqlalchemy.orm import Session
from core.response import resp_500
from core.logger import logger
from datetime import datetime, timedelta

from common.welab_k8s import init_k8s
from common.welab_k8s.k8s_deployment import K8sDeployment
from common.welab_k8s.k8s_statefulset import K8sStatefulset
from common.welab_k8s.k8s_utils import K8sUtils
from common.welab_k8s.k8s_node import K8sNode
from app.service_quality.models.qos_server_info import QosServiceInfo
from app.celery_task.models.celery_task import CeleryTask

from setting import config


class QosServerInfoHandler():
    """  ServerInfo Handler
    """
    def __init__(self):
        init_k8s()
        self.k8s_qos_namespace = config.K8S_QOS_NAMESPACE
        self.k8s_deployment = K8sDeployment()
        self.k8s_statefulset = K8sStatefulset()
        self.k8s_node = K8sNode()



    def get_server_info_search(self, db: Session, page: int, per_page: int, field: str, value: str):
        """
        根据表字段模糊匹配，获取服务列表信息
        """
        try:
            field_info = {field: value}
            order_info = {"classes": "asc", "name": "asc"}
            result = QosServiceInfo.filter_by_field_search(db, page, per_page, field_info, order_info)
            if not result:
                logger.error(f"get server info search, result is null => {field}:{value}")
                return False, None

        except Exception as e:
            message = f"get server info search by {field}:{value} error"
            logger.exception(f"{message} => {e}")
            return resp_500(message)

        return True, result




    def get_server_info_match(self, db: Session, page: int, per_page: int, field: str, value: str):
        """
        根据表字段完全匹配，获取服务列表信息
        """
        try:
            result = QosServiceInfo.filter_by_field_match(db, page, per_page, {field: value})
            if not result:
                logger.error(f"get server info match, result is null => {field}:{value}")
                return False

        except Exception as e:
            message = f"get server info match by {field}:{value} error"
            logger.exception(f"{message} => {e}")
            return resp_500(message)

        return result



    def get_server_info_count(self, db: Session):
        """
        获取应用服务根据classes聚合统计的数量信息
        """
        try:
            result = QosServiceInfo.group_by_classes(db)
            if not result:
                logger.error(f"get server info count from database failed")
                return False
        except Exception as e:
            message = f"get server info count error"
            logger.exception(f"{message} => {e}")
            return resp_500(message)

        return result




    def add_server_info(self, db: Session, data: dict):
        """
        创建服务信息
        """
        try:
            server_info_object = QosServiceInfo.get_object_by_name(db, data["name"])
            if server_info_object:
                # 如果存在， 则异常返回
                logger.error(f"add server info failed, name:{data['name']} was exist")
                return False

            result = QosServiceInfo.add(db, data)
            if not result:
                logger.error(f"add server info to database failed")
                return False
        except Exception as e:
            message = f"add server info error"
            logger.exception(f"{message} data:{data} => {e}")
            return resp_500(message)

        return True



    def update_server_info(self, db: Session, data: dict):
        """
        修改服务信息
        """
        try:
            server_info_object = QosServiceInfo.get_object_by_uuid(db, data['uuid'])
            if not server_info_object:
                # 如果不存在， 则异常返回
                logger.error(f"update server info failed, uuid:{data['uuid']} not exist")
                return False

            # 更新服务信息
            result = QosServiceInfo.update(db, data)
            if not result:
                logger.error(f"update server info to database failed")
                return False
        except Exception as e:
            message = f"update server info error"
            logger.exception(f"{message} data:{data} => {e}")
            return resp_500(message)

        return True



    def delete_server_info(self, db: Session, data: dict):
        """
        删除服务信息
        """
        try:
            server_info_object = QosServiceInfo.get_object_by_uuid(db, data['uuid'])
            if not server_info_object:
                # 如果不存在， 则异常返回
                logger.error(f"delete server info failed, uuid:{data['uuid']} not exist")
                return False


            # 删除告警指标
            QosServiceInfo.delete_object_by_uuid(db, data['uuid'])
        except Exception as e:
            message = f"delete server info error"
            logger.exception(f"{message} data:{data} => {e}")
            return resp_500(message)

        return True



    def get_server_port_by_type(self, type):
        port_info = {
            "java-service": 8080,
            "python-service": 8080,
            "node-service": 6000,
            "h5-service": 8000
        }

        return port_info.get(type, 0)


    def get_department_by_owner(self, owner):
        owner_info = {
            "devops": "系统运维",
            "toc-backend": "业务后台",
            "toc-business": "业务前端",
            "manage-system": "管理系统",
            "tianmian-cloudplat": "云平台",
            "tianmian-di": "数据智能",
            "tianmian-dataplat": "数据平台", 
            "tianmian-ai": "工人智能", 
            "tianmian-frontend": "基础研发",
            "innovation": "数据智能",
            "lender": "业务后台",
            "risk": "管理系统",
            "tob": "云平台",
            "toc": "业务前端",
            "architecture": "数据平台"
        }
        return owner_info.get(owner, "无部门")



    def get_server_from_deployment(self, namespace: str):
        """
        获取Deployment类型的服务
        """
        servers_info = []
        deployments_info = self.k8s_deployment.list_namespaced_deployment(namespace)
        if not deployments_info:
            logger.error(f"list namespaced deployment failed")
            return False


        for deployment_info in deployments_info:
            server_info = {
                "name": deployment_info.metadata.name,
                "type": deployment_info.spec.template.metadata.labels.get("type", ""),
                "classes": "k8s_deployment",
                "department": self.get_department_by_owner(deployment_info.spec.template.metadata.labels.get("owner", "")),
                "number": deployment_info.spec.replicas,
            }
            for container in deployment_info.spec.template.spec.containers:
                if container.name == server_info["name"]:
                    # cpu和内存的requests值，默认是数据库的0
                    if container.resources.requests:
                        server_info["cpu_request"] = K8sUtils.cpu_unit_to_G(container.resources.requests.get("cpu", "0"))
                        server_info["mem_request"] = K8sUtils.mem_unit_to_G(container.resources.requests.get("memory", "0"))

                    # cpu和内存的limits值，默认是数据库的0
                    if container.resources.limits:
                        server_info["cpu_limit"] = K8sUtils.cpu_unit_to_G(container.resources.limits.get("cpu", "0"))
                        server_info["mem_limit"] = K8sUtils.mem_unit_to_G(container.resources.limits.get("memory", "0"))

            server_info["port"]= self.get_server_port_by_type(server_info["type"])
            servers_info.append(server_info)

        return servers_info



    def get_server_from_statefulsets(self, namespace):
        """
        获取Statefulset类型的服务
        """
        servers_info = []
        statefulsets_info = self.k8s_statefulset.list_namespaced_statefulset(namespace)
        if not statefulsets_info:
            logger.error(f"list namespaced statefulset failed")
            return False

        for statefulset_info in statefulsets_info:
            server_info = {
                "name": statefulset_info.metadata.name,
                "type": statefulset_info.spec.template.metadata.labels.get("type", ""),
                "classes": "k8s_statefulset",
                "department": self.get_department_by_owner(statefulset_info.spec.template.metadata.labels.get("owner", "")),
                "number": statefulset_info.spec.replicas,
            }
            for container in statefulset_info.spec.template.spec.containers:
                if container.name == server_info["name"]:
                    # cpu和内存的requests值，默认是数据库的0
                    if container.resources.requests:
                        server_info["cpu_request"] = K8sUtils.cpu_unit_to_G(container.resources.requests.get("cpu", "0"))
                        server_info["mem_request"] = K8sUtils.mem_unit_to_G(container.resources.requests.get("memory", "0"))

                    # cpu和内存的limits值，默认是数据库的0
                    if container.resources.limits:
                        server_info["cpu_limit"] = K8sUtils.cpu_unit_to_G(container.resources.limits.get("cpu", "0"))
                        server_info["mem_limit"] = K8sUtils.mem_unit_to_G(container.resources.limits.get("memory", "0"))

            server_info["port"] = self.get_server_port_by_type(server_info["type"])
            servers_info.append(server_info)

        return servers_info



    def task_update_container_server_info_handle(self, db: Session):
        """
        更新容器服务信息的处理函数
        """
        try:
            # 获取容器服务:deployment列表
            deployment_servers_info = self.get_server_from_deployment(self.k8s_qos_namespace)
            if not deployment_servers_info:
                logger.error(f"get server from deployment failed")
                return False

            # 获取容器服务:statefulsets列表
            statefulset_servers_info = self.get_server_from_statefulsets(self.k8s_qos_namespace)
            if not statefulset_servers_info:
                logger.error(f"get server from statefulsets failed")
                return False

            # 容器服务列表
            servers_info = [*deployment_servers_info, *statefulset_servers_info]
            old_servers_info = QosServiceInfo.get_by_container(db)
            old_servers_name = {
                server_info["name"]: server_info
                for server_info in old_servers_info
            }

            # 添加容器服务信息到数据库
            for server_info in servers_info:
                if server_info["name"] in old_servers_name:
                    # 存在，则更新
                    server_info["uuid"] = old_servers_name[server_info["name"]]["uuid"]
                    del old_servers_name[server_info["name"]]
                    result = QosServiceInfo.update(db, server_info)
                else:
                    # 不存在，则新建
                    result = QosServiceInfo.add(db, server_info)
                if not result:
                    logger.error(f"server:{server_info['name']} add to database failed")
                    return False

            # 不存在的容器服务，则更新状态为下线
            for server_info in old_servers_name.values():
                server_info["state"] = False
                result = QosServiceInfo.update(db, server_info)
                if not result:
                    logger.error(f"server:{server_info['name']} update state failed")
                    return False

        except Exception as e:
            logger.error(f"update container server info error=>{e}")
            return False

        logger.info(f"update container server info success")
        return True




    def task_update_container_server_info_start(self, db: Session):
        """
        启动更新容器服务信息的异步任务
        """
        # 启动异步任务
        from app.service_quality.task import qos_update_container_server_info_async
        # 判断任务是否已经启动，控制只有一个任务在跑。 如果任务可以多个同时进行，则无需这一步判断。
        task_name = qos_update_container_server_info_async.name.split(".")[-1]
        if CeleryTask.is_progress_on_name(db, task_name):
            logger.error(f"task: {task_name} was stared")
            return False

        task = qos_update_container_server_info_async.delay()
        task_data = {
            "name": task_name,
            "tid": task.id,
            "state": task.state
        }

        logger.info(f"add task: {task_name} success")
        return task_data



    def post_server_info_task(self, db: Session):
        """
        启动同步容器服务信息数据到数据库的异步任务
        """
        try:
            result = self.task_update_container_server_info_start(db)
            if not result:
                logger.error(f"start task:post_server_info_task failed")
                return False

        except Exception as e:
            message = f"start task:post_server_info_task error"
            logger.exception(f"{message} => {e}")
            return resp_500(message)

        return result


    def get_department_ratio_for_container(self, db: Session):
        """
        根据cpu比例获取部门的占比信息
        """
        try:
            departments_cpu = QosServiceInfo.get_container_cpu_by_department(db)
            if not departments_cpu:
                logger.error(f"get container_cpu_by_department failed")
                return False

            cpu_total = 0
            for department_cpu in departments_cpu:
                cpu_total += department_cpu["cpu_total"]

            belong_department = {}
            for department_cpu in departments_cpu:
                belong_department[department_cpu["department"]] = department_cpu["cpu_total"]/cpu_total*100

        except Exception as e:
            message = f"get_department_ratio_for_container error"
            logger.exception(f"{message} => {e}")
            return resp_500(message)

        return belong_department



